/*
 * Copyright 2020 Rockchip Electronics Co. LTD
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */


// 用于采集唇部数据 
// 单次运行保存一次 70trials  320*240 pixels

#include <stdio.h>
#include <sys/poll.h>
#include <errno.h>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <vector>
#include <dirent.h>

#include "rk_defines.h"
#include "rk_debug.h"
#include "rk_mpi_vi.h"
#include "rk_mpi_mb.h"
#include "rk_mpi_sys.h"
#include "rk_mpi_venc.h"
#include "rk_mpi_vpss.h"
#include "rk_mpi_vo.h"
#include "rk_mpi_rgn.h"
#include "rk_mpi_cal.h"
#include "rk_mpi_ivs.h"

#include "rk_common.h"
#include "rk_comm_rgn.h"
#include "rk_comm_vi.h"
#include "rk_comm_vo.h"
#include "rk_comm_ivs.h"

#include "test_common.h"
#include "test_comm_utils.h"
#include "test_comm_argparse.h"
#include "test_comm_bmp.h"

using namespace std;

#define HAVE_VPSS
//#define HAVE_VO

#define TEST_VENC_MAX 2
#define TEST_WITH_FD 0
#define TEST_WITH_FD_SWITCH 0

#undef DBG_MOD_ID
#define DBG_MOD_ID RK_ID_VI

#define VIDEO_W 320
#define VIDEO_H 240
#define VIDEO_COUNT 70

// for 356x vo
#define RK356X_VO_DEV_HD0 0
#define RK356X_VO_DEV_HD1 1
#define RK356X_VOP_LAYER_CLUSTER_0 0
#define RK356X_VOP_LAYER_CLUSTER_1 2
#define RK356X_VOP_LAYER_ESMART_0 4
#define RK356X_VOP_LAYER_ESMART_1 5
#define RK356X_VOP_LAYER_SMART_0 6
#define RK356X_VOP_LAYER_SMART_1 7

#define TEST_VI_SENSOR_NUM 6

#define COMBO_START_CHN 4

typedef struct _rkTestVencCfg
{
    RK_BOOL bOutDebugCfg;
    VENC_CHN_ATTR_S stAttr; //编码通道属性结构体 编码器属性 码率控制器属性 GOP结构体
    RK_CHAR dstFilePath[128];
    RK_CHAR dstFileName[128];
    RK_S32 s32ChnId;
    FILE *fp;
    RK_S32 selectFd;
} TEST_VENC_CFG;

typedef struct rkVPSS_CFG_S
{
    RK_BOOL bOutDebugCfg;
    FILE *fp;
    RK_CHAR dstFilePath[128];
    RK_CHAR dstFileName[128];
    RK_S32 s32DevId;
    RK_S32 s32ChnId;
    RK_U32 u32VpssChnCnt;
    VPSS_GRP_ATTR_S stGrpVpssAttr;                   // VPSS GROUP 属性 输入图像的最大宽度 最大高度 像素格式 动态范围 组帧率控制 压缩方式
    VPSS_CHN_ATTR_S stVpssChnAttr[VPSS_MAX_CHN_NUM]; // VPSS 通道的属性 工作模式 宽度 高度 视频格式 像素格式 动态范围 压缩模式 帧率控制信息
    // 水平镜像使能 垂直反转使能 用户获取通道图像的队列长度 幅型比参数 最大目标图像缓冲个数
} VPSS_CFG_S;

typedef struct rkRGN_CFG_S
{
    RGN_ATTR_S stRgnAttr;        //定义区域属性结构体 区域类型 区域属性
    RGN_CHN_ATTR_S stRgnChnAttr; //马赛克区域的通道显示属性 区域是否显示 区域类型 通道显示属性
} RGN_CFG_S;

typedef enum rkTestVIMODE_E
{
    TEST_VI_MODE_VI_ONLY = 0,
    TEST_VI_MODE_BIND_VENC = 1,
    TEST_VI_MODE_BIND_VENC_MULTI = 2,
    TEST_VI_MODE_BIND_VPSS_BIND_VENC = 3,
    TEST_VI_MODE_BIND_VO = 4,
    TEST_VI_MODE_MUTI_VI = 5,
    TEST_VI_GET_SEND_VENC = 6,
    TEST_VI_GET_SEND_VO = 7,
    TEST_VI_MODE_BIND_VPSS_BIND_VO = 8,
    TEST_VI_MODE_BIND_VPSS_SEND_VO = 9,
} TEST_VI_MODE_E;

struct combo_th
{
    pthread_t th;
    RK_BOOL run;
    RK_S32 chn;
};

typedef struct _rkMpiVICtx
{
    RK_S32 width;     // 图像的宽
    RK_S32 height;    //图像的高
    RK_S32 devId;     //设备号
    RK_S32 pipeId;    //该 VI Dev 绑定的 PIPE 号
    RK_S32 channelId; //通道号

    RK_S32 vencChId;
    RK_S32 loopCountSet; //循环次数
    RK_S32 selectFd;

    RK_BOOL bFreeze; //是否使能视频输出冻结功能,VI模块输出的YUV画面保持不变，输出帧率等同于sensor输出帧率
    RK_BOOL bEnRgn;  //是否使能区域叠加
    RK_S32 s32RgnCnt;
    RK_S32 rgnType;
    RK_BOOL bMirror;     //定义 VI 通道属性，镜像使能开关
    RK_BOOL bFlip;       //定义 VI 通道属性，反转使能开关
    RK_BOOL bAttachPool; //是否创建内存缓存池
    MB_POOL attachPool;  //有效的缓存池 ID 号

    COMPRESS_MODE_E enCompressMode; //定义数据压缩格式结构体, 无压缩 AFBC压缩 无
    VI_DEV_ATTR_S stDevAttr;        //定义VI设备属性结构体 暂未使用
    VI_DEV_BIND_PIPE_S stBindPipe;  //定义 VI DEV 与 PIPE 的绑定关系, 与devID一致即可
    VI_CHN_ATTR_S stChnAttr;        //定义 VI 通道属性 获取图像宽高 像素格式 动态范围 视频数据格式 压缩格式 镜像使能
    //反转使能 队列深度 帧率控制 图像内存申请类型 获取图像为isp/直通数据的参数

    VI_SAVE_FILE_INFO_S stDebugFile; // VI通道数据保存信息结构体 是否保存 保存录路径 文件名 文件大小
    VIDEO_FRAME_INFO_S stViFrame;    //视频帧信息结构体 输出图像buffer结构体 宽度 高度 虚宽 虚高 枕场模式 图像像素格式 视频像素格式
    // 视频压缩格式 动态范围 色域范围 图像虚拟地址 图像帧序列号 时间戳 私有数据 当前帧标记
    VI_CHN_STATUS_S stChnStatus; // VI通道状态结构体 当前通道是否使能 输出帧率 丢失帧率 获取失败次数 图像大小
    TEST_VI_MODE_E enMode;       //当前demo的模式
    const char *aEntityName;     //通道设备名字，无赋值是默认chn0_out
    VI_CHN_BUF_WRAP_S stChnWrap; //定义VI通道buffer的卷绕属性 卷绕开关 buffer行高 buffer大小
    // for vi
    RGN_CFG_S stViRgn; //
    // for venc
    TEST_VENC_CFG stVencCfg[TEST_VENC_MAX];
    VENC_STREAM_S stFrame[TEST_VENC_MAX]; //帧码流类型结构体
    VPSS_CFG_S stVpssCfg;
    // for vo
    VO_LAYER s32VoLayer;
    VO_DEV s32VoDev;

    RK_U32 u32DstCodec;
    RK_BOOL bCombo;
    RK_S32 s32Snap;
    struct combo_th combo_th[TEST_VENC_MAX];

    RK_BOOL bRefBufShare;

    RK_U32 u32DeBreath;
    RK_U32 u32BitRateKb;
    RK_U32 u32BitRateKbMin;
    RK_U32 u32BitRateKbMax;
    RK_U32 u32DelayMsGet;
    RK_BOOL bIvs;
    RK_BOOL bSvc;
    RK_U32 u32GopSize;
} TAIIC_VI_CTX_S;

struct test_vi_save
{
    /* data */
    void *rawData;
    RK_U32 len;
    RK_U8 sNum;
    RK_U8 tNum;
    RK_U8 cNum;
};

//命名规则， session0_0_0.txt  batch数/trail数_数据编号.txt
// static void *saveDataSet(void *rawData, void *len, void *sNum, void *tNum, void *cNum)
static void *saveDataSet(void *args)
{

    struct test_vi_save *p = (test_vi_save *)args;

    FILE *pFile;

    char pathName[128] = {0};
    char name[256] = {0};

    RK_U8 sNum = p->sNum;
    RK_U8 tNum = p->tNum;
    RK_U8 cNum = p->cNum;
    RK_U64 len = p->len;
    void *rawData = p->rawData;

    memcpy(&pathName, "data", strlen("data"));
    snprintf(name, sizeof(name), "/%s/%s_%d_%d_%d.YUV",
             pathName, "session", sNum, tNum, cNum);
    RK_PRINT("======file name is %s =======\n", name);

    DIR *mydir = NULL;
    if ((mydir = opendir(pathName)) == NULL) //
    {
        bool ret = mkdir(pathName, 0755);
        printf("%s created sucess!/n", pathName);
    }

    if ((pFile = fopen(name, "wb")) == NULL)
    {
        printf("cant open the file");
        exit(0);
    }

    //可以写多个连续的数据(这里一次写len个)
    // fwrite (rawData , sizeof(double), len, pFile);

    fwrite(rawData, 1, len, pFile);
    fflush(pFile);
    sleep(1);
    fclose(pFile);

    return NULL;
}

void saveDataSet(void *rawData, int len, RK_U8 sNum, RK_U8 tNum, RK_U8 cNum)
{

    FILE *pFile;

    char pathName[128] = {0};
    char name[256] = {0};

    //   memcpy(&pathName,"data",strlen("data"));
    snprintf(pathName, sizeof(pathName), "/%s/%s_%d", "data", "session", sNum);
    snprintf(name, sizeof(name), "%s/%d_%d.YUV",
             pathName, tNum, cNum);
    RK_PRINT("======file name is %s =======\n", name);

    DIR *mydir = NULL;
    if ((mydir = opendir(pathName)) == NULL) //
    {
        bool ret = mkdir(pathName, 0755);
        printf("%s created sucess!\n", pathName);
    }

    if ((pFile = fopen(name, "wb")) == NULL)
    {
        printf("cant open the file\n");
        exit(0);
    }

    //可以写多个连续的数据(这里一次写len个)
    // fwrite (rawData , sizeof(double), len, pFile);

    fwrite(rawData, 1, len, pFile);
    fflush(pFile);
    sleep(1);
    fclose(pFile);
}

#ifdef HAVE_VPSS
static RK_S32 create_vpss(VPSS_CFG_S *pstVpssCfg, RK_S32 s32Grp, RK_S32 s32OutChnNum)
{
    RK_S32 s32Ret = RK_SUCCESS;
    VPSS_CHN VpssChn[VPSS_MAX_CHN_NUM] = {VPSS_CHN0, VPSS_CHN1, VPSS_CHN2, VPSS_CHN3};
    VPSS_CROP_INFO_S stCropInfo;

    //创建一个 VPSS GROUP
    s32Ret = RK_MPI_VPSS_CreateGrp(s32Grp, &pstVpssCfg->stGrpVpssAttr);
    RK_PRINT("=======CREATE VPSS=======\n");
    if (s32Ret != RK_SUCCESS)
    {
        return s32Ret;
    }

    for (RK_S32 i = 0; i < s32OutChnNum; i++)
    {
        //设置 VPSS 通道属性
        s32Ret = RK_MPI_VPSS_SetChnAttr(s32Grp, VpssChn[i], &pstVpssCfg->stVpssChnAttr[i]);
        if (s32Ret != RK_SUCCESS)
        {
            return s32Ret;
        }
        //启用 VPSS 通道
        s32Ret = RK_MPI_VPSS_EnableChn(s32Grp, VpssChn[i]);
        if (s32Ret != RK_SUCCESS)
        {
            return s32Ret;
        }
    }

    //使能 Backup 帧
    s32Ret = RK_MPI_VPSS_EnableBackupFrame(s32Grp);
    if (s32Ret != RK_SUCCESS)
    {
        return s32Ret;
    }

    //启用 VPSS GROU
    s32Ret = RK_MPI_VPSS_StartGrp(s32Grp);
    if (s32Ret != RK_SUCCESS)
    {
        return s32Ret;
    }

    return RK_SUCCESS;
}

static RK_S32 destory_vpss(RK_S32 s32Grp, RK_S32 s32OutChnNum)
{
    RK_S32 s32Ret = RK_SUCCESS;
    VPSS_CHN VpssChn[VPSS_MAX_CHN_NUM] = {VPSS_CHN0, VPSS_CHN1, VPSS_CHN2, VPSS_CHN3};

    s32Ret = RK_MPI_VPSS_StopGrp(s32Grp);
    if (s32Ret != RK_SUCCESS)
    {
        return s32Ret;
    }

    for (RK_S32 i = 0; i < s32OutChnNum; i++)
    {
        s32Ret = RK_MPI_VPSS_DisableChn(s32Grp, VpssChn[i]);
        if (s32Ret != RK_SUCCESS)
        {
            return s32Ret;
        }
    }

    s32Ret = RK_MPI_VPSS_DisableBackupFrame(s32Grp);
    if (s32Ret != RK_SUCCESS)
    {
        return s32Ret;
    }

    s32Ret = RK_MPI_VPSS_DestroyGrp(s32Grp);
    if (s32Ret != RK_SUCCESS)
    {
        return s32Ret;
    }

    return RK_SUCCESS;
}
#endif

static RK_S32 create_rgn(TAIIC_VI_CTX_S *ctx)
{
    RK_S32 i;
    RK_S32 s32Ret = RK_SUCCESS;
    RGN_ATTR_S stRgnAttr;
    RGN_CHN_ATTR_S stRgnChnAttr;
    RGN_HANDLE RgnHandle = 0;
    MPP_CHN_S stMppChn;
    RGN_TYPE_E rgnType = (RGN_TYPE_E)ctx->rgnType;

    /****************************************
     step 1: create overlay regions
    ****************************************/
    for (i = 0; i < ctx->s32RgnCnt; i++)
    {
        ctx->stViRgn.stRgnAttr.unAttr.stOverlay.u32ClutNum = 0;
        ctx->stViRgn.stRgnAttr.unAttr.stOverlay.stSize.u32Width = 128;
        ctx->stViRgn.stRgnAttr.unAttr.stOverlay.stSize.u32Height = 128;
        RgnHandle = i;
        s32Ret = RK_MPI_RGN_Create(RgnHandle, &ctx->stViRgn.stRgnAttr);
        if (RK_SUCCESS != s32Ret)
        {
            RK_LOGE("RK_MPI_RGN_Create (%d) failed with %#x!", RgnHandle, s32Ret);
            RK_MPI_RGN_Destroy(RgnHandle);
            return RK_FAILURE;
        }
        RK_LOGI("The handle: %d, create success!", RgnHandle);
    }

    /*********************************************
     step 2: display overlay regions to vi
    *********************************************/
    for (i = 0; i < ctx->s32RgnCnt; i++)
    {
        stMppChn.enModId = RK_ID_VI;
        stMppChn.s32DevId = ctx->devId;
        stMppChn.s32ChnId = ctx->channelId;
        RgnHandle = i;

        memset(&stRgnChnAttr, 0, sizeof(stRgnChnAttr));
        stRgnChnAttr.bShow = RK_TRUE;
        stRgnChnAttr.enType = rgnType;

        BITMAP_S stBitmap;
        switch (rgnType)
        {
        case COVER_RGN:
        {
            RGN_CHN_ATTR_S stCoverChnAttr;
            RK_LOGE("cover rgn attach");
            stCoverChnAttr.bShow = RK_TRUE;
            stCoverChnAttr.enType = COVER_RGN;
            stCoverChnAttr.unChnAttr.stCoverChn.stRect.s32X = 128 * i;
            stCoverChnAttr.unChnAttr.stCoverChn.stRect.s32Y = 128 * i;
            stCoverChnAttr.unChnAttr.stCoverChn.stRect.u32Width = 128;
            stCoverChnAttr.unChnAttr.stCoverChn.stRect.u32Height = 128;
            stCoverChnAttr.unChnAttr.stCoverChn.u32Color = 0xffffff; // white
            stCoverChnAttr.unChnAttr.stCoverChn.enCoordinate = RGN_ABS_COOR;
            stCoverChnAttr.unChnAttr.stCoverChn.u32Layer = i;

            s32Ret = RK_MPI_RGN_AttachToChn(RgnHandle, &stMppChn, &stCoverChnAttr);
            if (RK_SUCCESS != s32Ret)
            {
                RK_LOGE("failed with %#x!", s32Ret);
                goto __EXIT;
            }
            s32Ret = RK_MPI_RGN_GetDisplayAttr(RgnHandle, &stMppChn, &stCoverChnAttr);
            if (RK_SUCCESS != s32Ret)
            {
                RK_LOGE("failed with %#x!", s32Ret);
                goto __EXIT;
            }
            stCoverChnAttr.unChnAttr.stCoverChn.stRect.s32X = 128 * i;
            stCoverChnAttr.unChnAttr.stCoverChn.stRect.s32Y = 128 * i;
            stCoverChnAttr.unChnAttr.stCoverChn.stRect.u32Width = 128;
            stCoverChnAttr.unChnAttr.stCoverChn.stRect.u32Height = 128;
            stCoverChnAttr.unChnAttr.stCoverChn.u32Color = 0x0000ff; // blue
            stCoverChnAttr.unChnAttr.stCoverChn.enCoordinate = RGN_ABS_COOR;
            stCoverChnAttr.unChnAttr.stCoverChn.u32Layer = i;

            // change cover channel attribute below.
            s32Ret = RK_MPI_RGN_SetDisplayAttr(RgnHandle, &stMppChn, &stCoverChnAttr);
            if (RK_SUCCESS != s32Ret)
            {
                RK_LOGE("failed with %#x!", s32Ret);
                goto __EXIT;
            }
            RK_LOGI("the cover region %d to <%d, %d, %d, %d>, color<0x%x>",
                    RgnHandle,
                    stCoverChnAttr.unChnAttr.stCoverChn.stRect.s32X,
                    stCoverChnAttr.unChnAttr.stCoverChn.stRect.s32Y,
                    stCoverChnAttr.unChnAttr.stCoverChn.stRect.u32Width,
                    stCoverChnAttr.unChnAttr.stCoverChn.stRect.u32Height,
                    stCoverChnAttr.unChnAttr.stCoverChn.u32Color);
        }
        break;
        case MOSAIC_RGN:
        {
            RGN_CHN_ATTR_S stMoscaiChnAttr;
            RK_LOGE("mosaic rgn attach");
            stMoscaiChnAttr.bShow = RK_TRUE;
            stMoscaiChnAttr.enType = MOSAIC_RGN;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.s32X = 128 * i;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.s32Y = 128 * i;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.u32Width = 128;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.u32Height = 128;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.enBlkSize = MOSAIC_BLK_SIZE_8;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.u32Layer = i;

            s32Ret = RK_MPI_RGN_AttachToChn(RgnHandle, &stMppChn, &stMoscaiChnAttr);
            if (RK_SUCCESS != s32Ret)
            {
                RK_LOGE("failed with %#x!", s32Ret);
                goto __EXIT;
            }
            s32Ret = RK_MPI_RGN_GetDisplayAttr(RgnHandle, &stMppChn, &stMoscaiChnAttr);
            if (RK_SUCCESS != s32Ret)
            {
                RK_LOGE("failed with %#x!", s32Ret);
                goto __EXIT;
            }

            stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.s32X = 128 * i;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.s32Y = 128 * i;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.u32Width = 128;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.u32Height = 128;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.enBlkSize = MOSAIC_BLK_SIZE_8;
            stMoscaiChnAttr.unChnAttr.stMosaicChn.u32Layer = i;

            // change mosaic channel attribute below.
            s32Ret = RK_MPI_RGN_SetDisplayAttr(RgnHandle, &stMppChn, &stMoscaiChnAttr);
            if (RK_SUCCESS != s32Ret)
            {
                RK_LOGE("failed with %#x!", s32Ret);
                goto __EXIT;
            }
            RK_LOGI("the mosaic region:%d to <%d, %d, %d, %d>",
                    RgnHandle,
                    stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.s32X,
                    stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.s32Y,
                    stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.u32Width,
                    stMoscaiChnAttr.unChnAttr.stMosaicChn.stRect.u32Height);
        }
        break;
        default:
            break;
        }
    }

    return RK_SUCCESS;
__EXIT:
    for (i = 0; i < ctx->s32RgnCnt; i++)
    {
        stMppChn.enModId = RK_ID_VI;
        stMppChn.s32DevId = ctx->devId;
        stMppChn.s32ChnId = ctx->channelId;
        RgnHandle = i;
        s32Ret = RK_MPI_RGN_DetachFromChn(RgnHandle, &stMppChn);
        if (RK_SUCCESS != s32Ret)
        {
            RK_LOGE("RK_MPI_RGN_DetachFrmChn (%d) failed with %#x!", RgnHandle, s32Ret);
            return RK_FAILURE;
        }
    }
    return RK_FAILURE;
}

static MB_POOL create_pool(TAIIC_VI_CTX_S *pstCtx)
{
    MB_POOL_CONFIG_S stMbPoolCfg;
    PIC_BUF_ATTR_S stPicBufAttr;
    MB_PIC_CAL_S stMbPicCalResult;
    RK_S32 s32Ret = RK_SUCCESS;

    stPicBufAttr.u32Width = pstCtx->width;
    stPicBufAttr.u32Height = pstCtx->height;
    stPicBufAttr.enCompMode = (COMPRESS_MODE_E)pstCtx->enCompressMode;
    stPicBufAttr.enPixelFormat = (PIXEL_FORMAT_E)pstCtx->stChnAttr.enPixelFormat;

    RK_PRINT("======w is%d, h is %d, mode is %d, format is %d====\n", stPicBufAttr.u32Height, stPicBufAttr.u32Width,
             stPicBufAttr.enCompMode, stPicBufAttr.enPixelFormat);
    //计算VGS、VPSS输出所需图像buffer大小
    s32Ret = RK_MPI_CAL_VGS_GetPicBufferSize(&stPicBufAttr, &stMbPicCalResult);
    if (s32Ret != RK_SUCCESS)
    {
        RK_LOGE("get picture buffer size failed. err 0x%x", s32Ret);
        return MB_INVALID_POOLID;
    }

    memset(&stMbPoolCfg, 0, sizeof(MB_POOL_CONFIG_S));
    stMbPoolCfg.u64MBSize = stMbPicCalResult.u32MBSize;
    stMbPoolCfg.u32MBCnt = 3;
    stMbPoolCfg.enRemapMode = MB_REMAP_MODE_CACHED;
    stMbPoolCfg.bPreAlloc = RK_TRUE;

    //创建一个内存缓存池
    return RK_MPI_MB_CreatePool(&stMbPoolCfg);
}

/// <summary>
/// 初始化 vi
/// </summary>
/// <param name="ctx"></param>
/// <returns></returns>
static RK_S32 test_vi_init(TAIIC_VI_CTX_S *ctx)
{
    RK_S32 s32Ret = RK_FAILURE;

    // 0. get dev config status
    s32Ret = RK_MPI_VI_GetDevAttr(ctx->devId, &ctx->stDevAttr);
    // RK_PRINT("======00 results %X=====\n", s32Ret);
    if (s32Ret == RK_ERR_VI_NOT_CONFIG)
    {
        // 0-1.config dev
        s32Ret = RK_MPI_VI_SetDevAttr(ctx->devId, &ctx->stDevAttr);

        if (s32Ret != RK_SUCCESS)
        {
            RK_LOGE("RK_MPI_VI_SetDevAttr %x", s32Ret);
            goto __FAILED;
        }
        // RK_PRINT("=====0-1 %d=======\n", s32Ret);
    }
    else
    {
        // RK_PRINT("======GET DEV ATTR======\n");
        RK_LOGE("RK_MPI_VI_SetDevAttr already");
    }
    // 1.get  dev enable status
    s32Ret = RK_MPI_VI_GetDevIsEnable(ctx->devId);
    // RK_PRINT("=====11 %X======\n", s32Ret);
    if (s32Ret != RK_SUCCESS)
    {
        // 1-2.enable dev
        s32Ret = RK_MPI_VI_EnableDev(ctx->devId);
        if (s32Ret != RK_SUCCESS)
        {
            RK_LOGE("RK_MPI_VI_EnableDev %x", s32Ret);
            goto __FAILED;
        }
        // RK_PRINT("====== ENABLE DEV =======\n");
        // 1-3.bind dev/pipe
        ctx->stBindPipe.u32Num = ctx->pipeId;
        ctx->stBindPipe.PipeId[0] = ctx->pipeId;
        s32Ret = RK_MPI_VI_SetDevBindPipe(ctx->devId, &ctx->stBindPipe);
        if (s32Ret != RK_SUCCESS)
        {
            RK_LOGE("RK_MPI_VI_SetDevBindPipe %x", s32Ret);
            goto __FAILED;
        }
        // RK_PRINT("====== BIND PIPE========\n");
    }
    else
    {
        RK_LOGE("RK_MPI_VI_EnableDev already");
    }

    // 2.config channel
    ctx->stChnAttr.stSize.u32Width = ctx->width;
    ctx->stChnAttr.stSize.u32Height = ctx->height;
    ctx->stChnAttr.enCompressMode = ctx->enCompressMode;
    ctx->stChnAttr.u32Depth = 1;
    ctx->stChnAttr.bMirror = ctx->bMirror;
    ctx->stChnAttr.bFlip = ctx->bFlip;
    s32Ret = RK_MPI_VI_SetChnAttr(ctx->pipeId, ctx->channelId, &ctx->stChnAttr);

    if (s32Ret != RK_SUCCESS)
    {
        RK_LOGE("RK_MPI_VI_SetChnAttr %x", s32Ret);
        goto __FAILED;
    }
    // RK_PRINT("======22 %d=======\n", s32Ret);

    ctx->stViRgn.stRgnAttr.enType = (RGN_TYPE_E)ctx->rgnType;
    // RK_LOGE("RGN Type = %d", ctx->stViRgn.stRgnAttr.enType);
    ctx->stViRgn.stRgnChnAttr.bShow = RK_TRUE;

    // open fd before enable chn will be better

    // 3.enable channel
    s32Ret = RK_MPI_VI_EnableChn(ctx->pipeId, ctx->channelId);
    if (s32Ret != RK_SUCCESS)
    {
        RK_LOGE("RK_MPI_VI_EnableChn %x", s32Ret);
        goto __FAILED;
    }
    // RK_PRINT("======3. ENABLE CHANNE========\n");

    // 4.save debug file
    if (ctx->stDebugFile.bCfg)
    {
        s32Ret = RK_MPI_VI_ChnSaveFile(ctx->pipeId, ctx->channelId, &ctx->stDebugFile);
        // RK_LOGE("RK_MPI_VI_ChnSaveFile %x", s32Ret);
        // RK_PRINT("=====4. SAVE FILE========\n");
    }
    return RK_SUCCESS;

__FAILED:
    RK_PRINT("=====go to failed =====\n");
    return s32Ret;
}

static RK_S32 test_vi_get_release_frame_loop(TAIIC_VI_CTX_S *ctx)
{
    RK_S32 s32Ret;
    RK_S32 loopCount = 0;
    RK_S32 waitTime = 33;
    RK_BOOL bChangeRgn = RK_TRUE;

    /* test use getframe&release_frame */
    RK_U64 nowUstart = TEST_COMM_GetNowUs();
    s32Ret = test_vi_init(ctx);

    if (s32Ret != RK_SUCCESS)
    {
        RK_LOGE("vi %d:%d init failed:%x", ctx->devId, ctx->channelId, s32Ret);
        goto __FAILED;
    }
    // RK_PRINT("=======TEST VI INIT SUCCESS=======\n");

    // freeze test
    RK_MPI_VI_SetChnFreeze(ctx->pipeId, ctx->channelId, ctx->bFreeze);
    // RK_PRINT("======FREEZE TEST=======\n");

    while (loopCount < ctx->loopCountSet)
    {

        // 5.get the frame
        s32Ret = RK_MPI_VI_GetChnFrame(ctx->pipeId, ctx->channelId, &ctx->stViFrame, waitTime);
        // RK_PRINT("=====5. GET CHN FRAME========\n");
        if (s32Ret == RK_SUCCESS)
        {

            void *data = RK_MPI_MB_Handle2VirAddr(ctx->stViFrame.stVFrame.pMbBlk);
            // RK_PRINT("======data time is %lld ms=======\n", ctx->stViFrame.stVFrame.u64PTS / 1000);

            RK_U64 nowUend = TEST_COMM_GetNowUs();
            // RK_PRINT("======cost time is %lld ms====\n", (nowUend - nowUstart) / 1000);
            // 7.release the frame
            s32Ret = RK_MPI_VI_ReleaseChnFrame(ctx->pipeId, ctx->channelId, &ctx->stViFrame);
            // RK_PRINT("========7. RELEASE THE FRAME==========\n");
            if (s32Ret != RK_SUCCESS)
            {
                RK_LOGE("RK_MPI_VI_ReleaseChnFrame fail %x", s32Ret);
            }
            loopCount++;
            RK_PRINT("=====loopcount %d====== \n", loopCount);
        }
        else
        {
            RK_LOGE("RK_MPI_VI_GetChnFrame timeout %x", s32Ret);
        }

        usleep(20 * 1000);
    }

__FAILED:

    // 9. disable one chn
    s32Ret = RK_MPI_VI_DisableChn(ctx->pipeId, ctx->channelId);
    RK_LOGE("RK_MPI_VI_DisableChn %x", s32Ret);
    // 10.disable dev(will diabled all chn)
    s32Ret = RK_MPI_VI_DisableDev(ctx->devId);
    RK_LOGE("RK_MPI_VI_DisableDev %x", s32Ret);
    return s32Ret;
}

static void mpi_vi_test_show_options(const TAIIC_VI_CTX_S *ctx)
{
    RK_PRINT("cmd parse result:\n");

    RK_PRINT("output file open      : %d\n", ctx->stDebugFile.bCfg);
    RK_PRINT("yuv output file name  : %s/%s\n", ctx->stDebugFile.aFilePath, ctx->stDebugFile.aFileName);
    RK_PRINT("enc0 output file path : /%s/%s\n", ctx->stVencCfg[0].dstFilePath, ctx->stVencCfg[0].dstFileName);
    RK_PRINT("enc1 output file path : /%s/%s\n", ctx->stVencCfg[1].dstFilePath, ctx->stVencCfg[1].dstFileName);
    RK_PRINT("loop count            : %d\n", ctx->loopCountSet);
    RK_PRINT("ctx->enMode           : %d\n", ctx->enMode);
    RK_PRINT("ctx->dev              : %d\n", ctx->devId);
    RK_PRINT("ctx->pipe             : %d\n", ctx->pipeId);
    RK_PRINT("ctx->channel          : %d\n", ctx->channelId);
    RK_PRINT("ctx->width            : %d\n", ctx->width);
    RK_PRINT("ctx->height           : %d\n", ctx->height);
    RK_PRINT("enCompressMode        : %d\n", ctx->enCompressMode);
    RK_PRINT("enMemoryType          : %d\n", ctx->stChnAttr.stIspOpt.enMemoryType);
    RK_PRINT("ctx->aEntityName      : %s\n", ctx->stChnAttr.stIspOpt.aEntityName);
    RK_PRINT("ctx->depth            : %d\n", ctx->stChnAttr.u32Depth);
    RK_PRINT("enPixelFormat         : %d\n", ctx->stChnAttr.enPixelFormat);
    RK_PRINT("ctx->bFreeze          : %d\n", ctx->bFreeze);
    RK_PRINT("ctx->src_frame rate   : %d\n", ctx->stChnAttr.stFrameRate.s32SrcFrameRate);
    RK_PRINT("ctx->dst frame rate   : %d\n", ctx->stChnAttr.stFrameRate.s32DstFrameRate);
    RK_PRINT("out buf count         : %d\n", ctx->stChnAttr.stIspOpt.u32BufCount);
    RK_PRINT("venc ch start         : %d\n", ctx->vencChId);
    RK_PRINT("wrap mode enable      : %d\n", ctx->stChnWrap.bEnable);
    RK_PRINT("wrap line             : %d\n", ctx->stChnWrap.u32BufLine);

    RK_PRINT("ctx->bEnRgn           : %d\n", ctx->bEnRgn);
    RK_PRINT("rgn count             : %d\n", ctx->s32RgnCnt);
    RK_PRINT("rgn type              : %d\n", ctx->rgnType);
    RK_PRINT("enable mirror         : %d\n", ctx->bMirror);
    RK_PRINT("enable flip           : %d\n", ctx->bFlip);
    RK_PRINT("enable combo          : %d\n", ctx->bCombo);
    RK_PRINT("delay Ms Get          : %d\n", ctx->u32DelayMsGet);
    RK_PRINT("enable venc ref buf share : %d\n", ctx->bRefBufShare);
}

int main(int argc, const char *argv[])
{

    if (argc < 2)
    {
        RK_PRINT("Usage: lips_save 1 0");
        return -1;
    }

    RK_S32 sessionNum = 0;
    sessionNum = atoi(argv[1]);
    RK_S32 typeNUm = 0;
    typeNUm = atoi(argv[2]);
    // RK_PRINT("======session %d, type %d=======", sessionNum, typeNUm);

    RK_S32 i;
    RK_S32 s32Ret = RK_FAILURE;
    TAIIC_VI_CTX_S *ctx;                                                     //初始化变量
    ctx = reinterpret_cast<TAIIC_VI_CTX_S *>(malloc(sizeof(TAIIC_VI_CTX_S))); //分配内存空间
    memset(ctx, 0, sizeof(TAIIC_VI_CTX_S));                                  // 统一设置为0

    ctx->width = VIDEO_W;     //图像的宽
    ctx->height = VIDEO_H;    //图像的高
    ctx->devId = 0;           //设备id
    ctx->pipeId = ctx->devId; //管道id
    ctx->channelId = 0;       //通道id
    ctx->loopCountSet = VIDEO_COUNT;   //循环次数

    ctx->enMode = TEST_VI_MODE_VI_ONLY;
    // VI 通道属性
    ctx->stChnAttr.stIspOpt.u32BufCount = 3;
    ctx->stChnAttr.stIspOpt.enMemoryType = VI_V4L2_MEMORY_TYPE_DMABUF;
    ctx->stChnAttr.stIspOpt.bNoUseLibV4L2 = RK_TRUE;
    ctx->stChnAttr.u32Depth = 0;
    ctx->stChnAttr.enPixelFormat = RK_FMT_YUV420SP;
    ctx->stChnAttr.stFrameRate.s32SrcFrameRate = -1;
    ctx->stChnAttr.stFrameRate.s32DstFrameRate = -1;

    ctx->aEntityName = RK_NULL;
    ctx->u32DstCodec = RK_VIDEO_ID_HEVC;
    ctx->vencChId = 0;
    ctx->s32Snap = 1;

    ctx->bEnRgn = RK_FALSE;
    ctx->s32RgnCnt = 1;
    ctx->rgnType = RGN_BUTT;
    ctx->u32BitRateKb = 20 * 1024;
    ctx->u32BitRateKbMin = 20 * 1024;
    ctx->u32BitRateKbMax = 20 * 1024;
    ctx->bAttachPool = RK_FALSE;
    ctx->u32DelayMsGet = 0;
    ctx->u32GopSize = 60;
    ctx->stDebugFile.bCfg = RK_TRUE;

    memcpy(&ctx->stDebugFile.aFilePath, "/data/lips", strlen("/data/lips"));

    DIR *mydir = NULL;
    if ((mydir = opendir(ctx->stDebugFile.aFilePath)) == NULL) //
    {
        bool ret = mkdir(ctx->stDebugFile.aFilePath, 0755);
        printf("%s created sucess!/n", ctx->stDebugFile.aFilePath);
    }

    // snprintf(ctx->stDebugFile.aFileName, MAX_VI_FILE_PATH_LEN,
    //              "session_%d_%d_%d.bin", ctx->devId, ctx->pipeId, ctx->channelId);
    snprintf(ctx->stDebugFile.aFileName, MAX_VI_FILE_PATH_LEN,
             "session%d_%d.YUV", sessionNum, typeNUm);

    // RK_PRINT("==== FILE IS %s========\n", ctx->stDebugFile.aFileName);

    // RK_LOGE("test running enter ctx->aEntityName=%s!\n", ctx->aEntityName);
    if (ctx->aEntityName != RK_NULL)
        memcpy(ctx->stChnAttr.stIspOpt.aEntityName, ctx->aEntityName, strlen(ctx->aEntityName));

    // 打印参数列表
    // mpi_vi_test_show_options(ctx);
    system("echo 1 >/sys/class/pwm/pwmchip1/pwm0/enable"); //开启灯
    system("echo b > /sys/devices/virtual/adw/adwdev/adwgpio");

    sleep(0.2);
    // 初始化rk 接口
    if (RK_MPI_SYS_Init() != RK_SUCCESS)
    {
        RK_LOGE("rk mpi sys init fail!");
        goto __FAILED;
    }
    // RK_PRINT("=============INIT====================\n");

    if (!ctx->stChnAttr.u32Depth)
    {
        RK_LOGE("depth need > 0 when vi not bind any other module!\n");
        ctx->stChnAttr.u32Depth = ctx->stChnAttr.stIspOpt.u32BufCount;
        RK_PRINT("=====depth is %d=======\n", ctx->stChnAttr.u32Depth);
    }
    // RK_PRINT("=============RUN====================\n");
    s32Ret = test_vi_get_release_frame_loop(ctx);

    system("echo 0 >/sys/class/pwm/pwmchip1/pwm0/enable"); //关闭灯
    system("echo a > /sys/devices/virtual/adw/adwdev/adwgpio");

__FAILED:
    RK_LOGE("test running exit:%d", s32Ret);
    RK_MPI_SYS_Exit();

__FAILED2:
    if (ctx)
    {
        free(ctx);
        ctx = RK_NULL;
    }

    return 0;
}
